#include "ThreadChunks.h"
#include "Client.h"

ThreadChunks::ThreadChunks(Client* cli) : ClientThread(cli, "ThreadChunks") {
	client = cli;
	ticks = geGetTick();
	last_x = (int)client->cam->x-1;
	last_y = (int)client->cam->y-1;
}

void ThreadChunks::run(){
	if(client->getWorld()->getGameMode() == SOLO){
		ChunksSolo();
	}else{
		ChunksMulti();
	}
//	ticks = geWaitTick(100, ticks);
}

void ThreadChunks::EnqueueChunkBuild(Chunk* chunk){
	chunks_build.push_back(chunk);
}

int ThreadChunks::getQueueSize(){
	return chunks_build.size();
}

void ThreadChunks::ChunksSolo(){
	World* world = client->getWorld();
	ge_Camera* cam = client->cam;

	if((int)cam->x == last_x && (int)cam->y == last_y){
		if(client->options.multicore){
			ticks = geWaitTick(50, ticks);
		}else{
			ticks = geWaitTick(100, ticks);
		}
		return;
	}
	
	int last_x2 = (int)cam->x;
	int last_y2 = (int)cam->y;

	int x0, y0, d;
	for(d=8; d<=std::max(client->options.view_distance, client->options.extended_view); d+=16){
		client->options.curr_view_dist = d;
		int ystep = 16;
		for(y0=-d; y0<=d; y0+=ystep){
			int xstep = 2 * d;
			if(y0 == -d || y0 == d){
				xstep = 16;
			}
			for(x0=-d; x0<=d; x0+=xstep){
				if((int)cam->x != last_x2 || (int)cam->y != last_y2){
					last_x2 = (int)cam->x;
					last_y2 = (int)cam->y;
					if(client->options.multicore){
						ticks = geWaitTick(1, ticks);
					}else{
						ticks = geWaitTick(2, ticks);
					}
					d = client->options.view_distance * 2;
					y0 = d * 3;
					break;
				}
				int x = (int)client->cam->x + x0;
				int y = (int)client->cam->y + y0;
				if(!world->FindChunk(x, y)){
					Chunk* chunk = world->GetUnusedChunk();
					if(!chunk){
						chunk = world->GetMostAwayChunk(cam->x, cam->y);
						if(chunk->getX() == (x & 0xFFFFFFF0) && chunk->getY() == (y & 0xFFFFFFF0)){
							chunk = NULL;
						}
					}
					if(chunk){
						chunk->setVisible(false);
						chunk->getRenderer()->nVerts = 0;
						chunk->setX((x >> 4) << 4);
						chunk->setY((y >> 4) << 4);
						if(!chunk->Load(x, y)){
						//	GenerateChunk(chunk, chunk_i, client->options.seed);
							continue;
						}
						/*
						if(d > client->options.view_distance){
							chunk->MakeFarSides(1);
							chunk->setX(-1);
							chunk->setY(-1);
						}else{
							chunk->MakeSides();
						}
						*/
						//chunk->MakeFarSides(2);
						chunk->MakeSides();
						client->getThreadRender()->EnqueueChunkUpdate(chunk);
					}
					if(d < client->options.view_distance * 0.25){
						ticks = geWaitTick(5, ticks);
					}else{
						ticks = geWaitTick(50, ticks);
					}
				}else{
					if(client->options.multicore){
					//	ticks = geWaitTick(1, ticks);
					}else{
						ticks = geWaitTick(2, ticks);
					}
				}
			}
		}
	}
	/*
	int last_x2 = (int)cam->x;
	int last_y2 = (int)cam->y;
	float a = 0.0;
	float a2 = 0.0;
	float da = 0.5;
	float dist = 0.0;
	for(dist=0.0; dist<client->options.view_distance; dist+=8.0, da *= 0.8){
		client->options.curr_view_dist = (int)dist;
		for(a=0.0; a<M_PI; a+=M_PI*da){
			if((int)cam->x != last_x2 || (int)cam->y != last_y2){
			//	dist = world->options.view_distance*4.0;
				dist = 0.0;
				da = 0.5;
				last_x2 = (int)cam->x;
				last_y2 = (int)cam->y;
				if(client->options.multicore){
					ticks = geWaitTick(1, ticks);
				}else{
					ticks = geWaitTick(2, ticks);
				}
				break;
			}
			for(a2=-a; a2<2*a; a2+=a){
				int x = cam->x + dist * geCos(a2);
				int y = cam->y + dist * geSin(a2);
				if(!world->FindChunk(x, y)){
					Chunk* chunk = world->GetUnusedChunk();
					if(!chunk){
						chunk = world->GetMostAwayChunk(cam->x, cam->y);
						if(chunk->getX() == (x & 0xFFFFFFF0) && chunk->getY() == (y & 0xFFFFFFF0)){
							chunk = NULL;
						}
					}
					if(chunk){
						chunk->setVisible(false);
						chunk->getRenderer()->nVerts = 0;
						chunk->setX((x >> 4) << 4);
						chunk->setY((y >> 4) << 4);
						if(!chunk->Load(x, y)){
						//	GenerateChunk(chunk, chunk_i, client->options.seed);
							continue;
						}
						chunk->MakeSides();
						client->getThreadRender()->EnqueueChunkUpdate(chunk);
					}
					if(dist < client->options.view_distance * 0.25){
						ticks = geWaitTick(5, ticks);
					}else{
						ticks = geWaitTick(50, ticks);
					}
				}else{
					if(client->options.multicore){
					//	ticks = geWaitTick(1, ticks);
					}else{
						ticks = geWaitTick(2, ticks);
					}
				}
			}
		}
	}
	*/
	while((int)cam->x == last_x && (int)cam->y == last_y){
		ticks = geWaitTick(10, ticks);
	}
	last_x = (int)cam->x;
	last_y = (int)cam->y;
}

void ThreadChunks::ChunksMulti(){
	if(client->options.multicore){
		ticks = geWaitTick(10, ticks);
	}else{
		ticks = geWaitTick(20, ticks);
	}
	while(chunks_build.size() > 0){
		Chunk* chunk = chunks_build.front();
		//chunk->setVisible(false);
		//chunk->getRenderer()->enabled = false;
		chunk->MakeSides();
		client->getThreadRender()->EnqueueChunkUpdate(chunk);
		Chunk* c1 = client->getWorld()->FindChunk(chunk->getX() - 16, chunk->getY() - 16);
		Chunk* c2 = client->getWorld()->FindChunk(chunk->getX() - 16, chunk->getY() + 16);
		Chunk* c3 = client->getWorld()->FindChunk(chunk->getX() + 16, chunk->getY() - 16);
		Chunk* c4 = client->getWorld()->FindChunk(chunk->getX() + 16, chunk->getY() + 16);
		/*
		if(c1){
			c1->MakeSides();
			client->getThreadRender()->EnqueueChunkUpdate(c1);
		}
		if(c2){
			c1->MakeSides();
			client->getThreadRender()->EnqueueChunkUpdate(c2);
		}
		if(c3){
			c1->MakeSides();
			client->getThreadRender()->EnqueueChunkUpdate(c3);
		}
		if(c4){
			c1->MakeSides();
			client->getThreadRender()->EnqueueChunkUpdate(c4);
		}
		*/
		chunks_build.pop_front();
		if(client->options.multicore){
			ticks = geWaitTick(10, ticks);
		}else{
			ticks = geWaitTick(20, ticks);
		}
	}
}
